import wx
import os
from pyeasyaccept.gui.ChooseScriptsPanel import ChooseScriptsPanel
from pyeasyaccept.gui.ConsolePanel import ConsolePanel
from pyeasyaccept.gui.ChooseFacadePanel import ChooseFacadePanel
from pyeasyaccept.PythonEasyAcceptFacade import PythonEasyAcceptFacade
from pyeasyaccept.commom.Configuration import Configuration
from pyeasyaccept.gui.CreateTestsFrame import CreateTestsFrame
from pyeasyaccept.gui.ErrorMsg import ErrorMsg
from pyeasyaccept.commom.FacadeFactory import FacadeFactory
from pyeasyaccept.commom.FindScript import FindScript
from pyeasyaccept.gui.TestingProject import TestingProject

class RunTestsFrame(wx.Frame):
    """
    This class represents a frame that allows to choose the Facade and scripts that will be use to run the tests.
    @author: Danilo Torres
    @author: Mikaela Maia
    @author: Artur Farias
    """    

    # Menus Constants
    MENU_CHOOSE_FOLDER_ID = 1
    MENU_HELP_ID = 2
    MENU_EXIT_ID = 3 
    MENU_ABOUT_ID = 4
    MENU_COMMANDS_ID = 5
    MENU_NEW_ID = 6
    MENU_OPEN_ID = 7
    MENU_SAVE_ID = 8
    MENU_SAVE_AS_ID = 9
    
    # Windows IDs
    CHOOSE_SCRIPTS_PANEL_ID = 100
    CONSOLE_PANEL_ID = 101
    RUN_TESTS_ID = 102
    FACADE_PANEL_ID = 103
    SCROLLED_WINDOW_ID = 104
    PANEL_INF_ID = 105
    TOOLBAR_RUN_ID = 106
    LINE_SEPARATOR = os.linesep
    
    # Confirmation answer IDs
    YES_ID = 1
    NO_ID = 2
    CANCEL_ID = 3
    
    def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize):
        """
        Initializes a RunTestsFrame with all the components needed to run tests.
        @param parent: the Frame parent.
        @param id: the Frame id.
        @param title: the Frame title.
        @param pos: the Frame intial position.
        @param: size: the Frame intial size.
        """
        wx.Frame.__init__(self, parent, id, title, pos, size)
        self.SetIcon(wx.Icon(Configuration.INSTALL_DIR + Configuration.ICONS_LOCATION + Configuration.FILE_SEPARATOR + 'IconPEA.ico', wx.BITMAP_TYPE_ICO))
        self.createMenu()
        self.initializeTestingProject()
        self.Centre()
        self.Layout()    
        self.SetAutoLayout(True)
        
    def createMenu(self):
        """"Method that creates a menu"""
        menubar = wx.MenuBar()
        menu = wx.Menu()
      
        helpMenu = wx.Menu()
        helpMenu.Append(self.MENU_ABOUT_ID, '&About')
        helpMenu.Append(self.MENU_COMMANDS_ID, '&Commands')
        
        
        menu.Append(self.MENU_NEW_ID, 'New', '')
        menu.Append(self.MENU_OPEN_ID, 'Open', '')
        menu.Append(self.MENU_SAVE_ID, 'Save', '')
        menu.Append(self.MENU_SAVE_AS_ID, 'Save As...', '')
        menu.AppendSeparator()
        menu.Append(self.MENU_CHOOSE_FOLDER_ID, '&Choose a script folder', '')
        menu.AppendSeparator()
        menu.Append(self.MENU_EXIT_ID,'&Exit','exiting')
        
        menubar.Append(menu, '&File')
        menubar.Append(helpMenu, '&Help')
        
        self.SetMenuBar(menubar)
        self.bindMenuEvents()
        
    def bindMenuEvents(self):       
        self.Bind(wx.EVT_CLOSE, self.OnQuit)
        self.Bind(wx.EVT_MENU, self.OnChooseScriptFolder, id = self.MENU_CHOOSE_FOLDER_ID)
        self.Bind(wx.EVT_MENU, self.OnQuit, id = self.MENU_EXIT_ID)
        self.Bind(wx.EVT_MENU, self.OnAbout, id = self.MENU_ABOUT_ID)
        self.Bind(wx.EVT_MENU, self.OnCommands, id = self.MENU_COMMANDS_ID)
        self.Bind(wx.EVT_MENU, self.onNewProject, id=self.MENU_NEW_ID)
        self.Bind(wx.EVT_MENU, self.onOpenProject, id=self.MENU_OPEN_ID)
        self.Bind(wx.EVT_MENU, self.onSaveProject, id=self.MENU_SAVE_ID)
        self.Bind(wx.EVT_MENU, self.onSaveProjectAs, id=self.MENU_SAVE_AS_ID)
        
    def initializeTestingProject(self):
        self.testingProject = TestingProject(None)
        self.askBeforeCloseProject = False
        self.notebook = wx.Notebook(self, -1, size=self.GetSize())
        
        self.consolePanel = ConsolePanel(self.notebook, self.CONSOLE_PANEL_ID)
        
        self.scrolledWindow = wx.ScrolledWindow(self.notebook, self.SCROLLED_WINDOW_ID, style=wx.SIMPLE_BORDER)
        self.panelInf = wx.Panel(self.scrolledWindow, self.PANEL_INF_ID)
        self.chooseFacade = ChooseFacadePanel(self.panelInf, self.FACADE_PANEL_ID, [1, 2], True)
        self.chooseFacade.addListener(self)
        self.chooseTestScripts = ChooseScriptsPanel(self.panelInf, self.CHOOSE_SCRIPTS_PANEL_ID)
        self.chooseTestScripts.addListener(self)
        self.run = wx.Button(self.panelInf, self.RUN_TESTS_ID, 'Run Tests')
        self.createScriptButton = wx.Button(self.panelInf, -1, "Create a Script Test")

        horSizer = wx.BoxSizer(wx.HORIZONTAL)
        horSizer.Add(self.chooseFacade)
        horSizer.Add(wx.StaticText(self.panelInf, -1, '  '))
        horSizer.Add(self.createScriptButton, 0, wx.ALIGN_LEFT|wx.TOP)
        horSizer.Add(wx.StaticText(self.panelInf, -1, '  '))
        horSizer.Add(self.run, 0, wx.ALIGN_RIGHT|wx.TOP)
        
        vertSizer = wx.BoxSizer(wx.VERTICAL)
        vertSizer.Add(wx.StaticText(self.panelInf, -1, ''), 0, wx.ALIGN_CENTER_HORIZONTAL)

        vertSizer.Add(horSizer, 0, wx.ALIGN_CENTER_HORIZONTAL)
        vertSizer.Add(wx.StaticText(self.panelInf, -1, ''), 0, wx.ALIGN_CENTER_HORIZONTAL)
        vertSizer.Add(self.chooseTestScripts, 0, wx.ALIGN_CENTER_HORIZONTAL)
        
        self.panelInf.SetSizer(vertSizer)
        
        swSizer = wx.BoxSizer(wx.VERTICAL)
        swSizer.Add(self.panelInf, 0, wx.ALIGN_CENTER)
        self.scrolledWindow.SetSizer(swSizer)

        self.notebook.AddPage(self.scrolledWindow, "Choose Scripts")
        self.notebook.AddPage(self.consolePanel, "Results")
        
        self.setBackGroundColour(Configuration.BACKGROUND_COLOUR)
        
        self.setAutoLayout(True)
        
        self.run.Bind(wx.EVT_BUTTON, self.runAll)
        self.createScriptButton.Bind(wx.EVT_BUTTON, self.createScriptTest)
        self.panelInf.Bind(wx.EVT_SIZE, self.reSizePanelInf)
        self.chooseTestScripts.Bind(wx.EVT_SIZE, self.reSizeChooseTestScripts)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        sizer.Add(self.notebook, 1, wx.EXPAND)
        sizer.SetMinSize(wx.Size(900,600))
        self.SetSizerAndFit(sizer)
        self.onNewProject(None)
        
    def setAutoLayout(self, autoLayout):
        self.SetAutoLayout(autoLayout)
        self.notebook.SetAutoLayout(autoLayout)
        self.scrolledWindow.SetAutoLayout(autoLayout)
        self.panelInf.SetAutoLayout(autoLayout)
        self.chooseFacade.SetAutoLayout(autoLayout)
        self.chooseTestScripts.SetAutoLayout(autoLayout)
        
    def setBackGroundColour(self, colour):
        self.chooseFacade.SetBackgroundColour(colour)
        self.chooseTestScripts.SetBackgroundColour(colour)
        self.panelInf.SetBackgroundColour(colour)
        self.scrolledWindow.SetBackgroundColour(colour)
        
    def createToolBar(self):
         """
         Creates a ToolBar for this Frame.
         """
         projectPath = Configuration.getInstance().getProjectRoot()
         self.toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER)
         self.toolbar.AddSimpleTool(self.TOOLBAR_RUN_ID, wx.Bitmap(Configuration.INSTALL_DIR + Configuration.ICONS_LOCATION + Configuration.FILE_SEPARATOR + 'my_run.png') ,'Run All Tests', '')
         self.toolbar.Realize()
         self.toolbar.Bind(wx.EVT_TOOL, self.toolBarRunAll)
         
    def addScriptTest(self, scriptTest):
        self.chooseTestScripts.addScriptTest(scriptTest)

    def reSizePanelInf(self, event):
        """
        It sets a ScrollBar, if it is needed, when receive a SizeEvent from the panelInf.
        @param event: a SizeEvent.
        """
        if event.GetId() != self.PANEL_INF_ID:
            event.Skip()
            return
        
        sizePanelInf = self.panelInf.GetSize()
        parentSize = self.panelInf.GetParent().GetSize()
        if sizePanelInf[1] > parentSize[1]: 
            self.scrolledWindow.SetScrollbars(0, 1 , 0, sizePanelInf[1], 0, sizePanelInf[1])
        
    def reSizeChooseTestScripts(self, event):
        """
        It removes a ScrollBar, if it isn't needed any more, when receive a SizeEvent from the panelInf.
        @param event: a SizeEvent.
        """        
        if event.GetId() != self.CHOOSE_SCRIPTS_PANEL_ID:
            event.Skip()
            return

        sizePanelInf = self.panelInf.GetSize()
        parentSize = self.panelInf.GetParent().GetSize()
        if (sizePanelInf[1] > parentSize[1]):
            self.scrolledWindow.SetScrollbars(0,0,0,0)
            
    def createScriptTest(self, event):
        createFrame = CreateTestsFrame(self, -1, "Create Script Test", size=wx.Size(800,600))
        if self.testingProject != None and self.testingProject.facade != None:
            createFrame.setFacade(self.testingProject.getFacade())
        createFrame.Show(True)            
        createFrame.addListener(self)
    
    def OnChooseScriptFolder(self, event):
        dlg = wx.DirDialog(self, "Choose a directory:")
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            list = FindScript().getScripts(path)
            self.chooseTestScripts.addTestScripts(list)
        dlg.Destroy()
        
        
    def onSaveProject(self, event):
        if not self.askBeforeCloseProject:
            return
        file = self.testingProject.getFile()
        if file == None:
            self.saveProjectAs()
        else:
            scripts = self.chooseTestScripts.getScriptsToSave()
            self.testingProject.save(file, scripts)
            self.askBeforeCloseProject = False
        
    def onSaveProjectAs(self, event):
        self.saveProjectAs()
        
    def saveProjectAs(self):
        path = Configuration.PATH_TO_BROWSE
        dlg = wx.FileDialog(self, "Save Testing Project", path, "", "*.pea", wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
        if dlg.ShowModal() == wx.ID_OK:
            fileToSave = dlg.GetPath()
            scriptsToSave = self.chooseTestScripts.getScriptsToSave()
            self.testingProject.save(fileToSave, scriptsToSave)
            self.askBeforeCloseProject = False
        dlg.Destroy() 
        
    def onOpenProject(self, event):
        if not self.confirmClosingProject():
            return
              
        path = Configuration.PATH_TO_BROWSE
        dlg = wx.FileDialog(self, "Open Testing Project", path, "", "*.pea", wx.FD_OPEN)
        fileToOpen = None
        if dlg.ShowModal() == wx.ID_OK:
            fileToOpen = dlg.GetPath()
            self.testingProject = TestingProject(None)
            self.testingProject.open(fileToOpen)
            self.chooseFacade.setFacade(self.testingProject.getFacade())
            self.chooseTestScripts.setTestScripts(self.testingProject.getTestScripts())
            self.askBeforeCloseProject = False
        dlg.Destroy()         
        
    def confirmClosingProject(self):
        if not self.askBeforeCloseProject:
            return True
        confirmation = self.askConfirmationToClose()
        if confirmation == self.YES_ID:
            self.saveProjectAs()
                    
        if confirmation == self.CANCEL_ID:
            return False
        else:
            return True
        
    def onNewProject(self, event):
        if not self.confirmClosingProject():
            return
              
        self.chooseFacade.reset()
        self.chooseTestScripts.reset()
        self.testingProject = TestingProject(None)
        self.askBeforeCloseProject = False

    def askConfirmationToClose(self):
        dlg = wx.MessageDialog(self, 'Your testing project has been modified. Save changes?', 'Save Project', wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
        answer = dlg.ShowModal()
        if answer == wx.ID_YES:
            confirmationResult = self.YES_ID
        elif answer == wx.ID_NO:
            confirmationResult = self.NO_ID
        else:
            confirmationResult = self.CANCEL_ID
        dlg.Destroy()
        return confirmationResult
        
    def notifyScriptTestAdded(self, scriptTest):
        self.askBeforeCloseProject = True
        
    def notifyCheckBoxClicked(self):
        self.askBeforeCloseProject = True
        
    def notifyScriptRemoved(self):
        self.askBeforeCloseProject = True

    def notifyScriptLineAdded(self):
        self.panelInf.Layout()
        self.scrolledWindow.Layout()
        
    def notifySetFacade(self, facade):
        if not self.isValidFacade(facade):
            return
        
        self.chooseFacade.setFacade(facade)
        self.askBeforeCloseProject = True
        
        if self.testingProject == None:
            self.testingProject = TestingProject(facade)
        else:
            self.testingProject.setFacade(facade)        
    
    def OnQuit(self, event):
        """Method that close the window on click 'exit' at menu"""
        if not self.confirmClosingProject():
            return
                
        self.Destroy()
   
    def OnAbout(self, event):
        """Method that show a message dialog with 'about' information on click 'about' at menu"""
        about = "   Python EasyAccept is a tool that helps you create and run acceptance tests in an easy, quick, and clean way for Python applications, bridging the communication gap between clients and software developers.\n\n\n   Python EasyAccept is being developed in an open source project under GNU license, held in SourceForge. Feel free to join the project and help us improve the product. More about Python EasyAccept in http://www.pyeasyaccept.org"
        dlg = wx.MessageDialog(self, about , 'About', wx.OK|wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def OnCommands(self, event):
        """Method that show a message dialog with the internal commands on click 'commands' at menu"""
        containsstring = 'containsstring: Built-in command that is used to check if a first string argument contains the returned object, when converted to a string.' + self.LINE_SEPARATOR + self.LINE_SEPARATOR
        expect = "expect: Built-in command that is used to check that a (business logic) command produced the expected result."+ self.LINE_SEPARATOR+ self.LINE_SEPARATOR
        expectbetween = "expectBetween: Built-in command that is used to check if the returned object is between the interval expected."+ self.LINE_SEPARATOR+ self.LINE_SEPARATOR
        expectDifferent = "expectDifferent: Built-in command that is used to check that a (business logic) command produced a different result from a given described result."+ self.LINE_SEPARATOR+ self.LINE_SEPARATOR
        expectError = "expectError: Built-in command that is used to check error conditions of a (business logic) command."+ self.LINE_SEPARATOR+ self.LINE_SEPARATOR
        equalFiles = "equalsFiles: Built-in command that compares two files."+ self.LINE_SEPARATOR+ self.LINE_SEPARATOR
        echo = "echo: Built-in command that returns the concatenation of its parameters."+ self.LINE_SEPARATOR+ self.LINE_SEPARATOR
        commands = containsstring+expect+expectbetween+expectDifferent+expectError+equalFiles+echo 
        dlg = wx.MessageDialog(self, commands, 'Commands', wx.OK|wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()
        
    def isValidFacade(self, facade):
        """Method that returns if the facade is valid or not"""
        return facade != None and facade != ''
    
    def toolBarRunAll(self, event):
        """
        Run all tests by receiving a ToolEvent.
        """
        if (event.GetId() != self.TOOLBAR_RUN_ID):
            event.Skip()
            return
        self.runTests()
        
    def notifyRunTest(self, facade, testScript):
        if facade != self.getFacade():
            self.onNewProject(None)
            self.chooseFacade.setFacade(facade)
            self.testingProject.setFacade(facade)
            
        allTestScripts = self.chooseTestScripts.getAllTestScripts()             
        if len(allTestScripts) == 0:
            self.chooseTestScripts.removeAllTests()
            
        if not allTestScripts.__contains__(testScript):
            self.addScriptTest(testScript)
        self.runTests()
            
    def runTests(self):
        """Method that run all tests"""
        facade = self.testingProject.getFacade()
        if not self.isValidFacade(facade):
            ErrorMsg(self, ErrorMsg.FACADE_NOT_VALID)
            return
            
        try:
            facadeInstance = FacadeFactory().getFacadeInstance(facade)
        except Exception, e:
            ErrorMsg(self, ErrorMsg.FACADE_NOT_FOUND)
            return
        
        testScripts = self.chooseTestScripts.getTestScripts()
        self.peaFacade = PythonEasyAcceptFacade(facadeInstance, testScripts)
        
        try:
            self.peaFacade.executeTests()
        except Exception, e: 
            ErrorMsg(self, str(e))
            return
            
        result = self.peaFacade.getCompleteResults()
        self.consolePanel.setResult(result)
        self.notebook.SetSelection(1)        
        self.consolePanel.SetFocus()
        self.Restore()
        
    def runAll(self, event):
        """
        Run all tests by receiving a ButtonEvent.
        """
        self.runTests()
        
    def getFacade(self):
        """
        Returns the facade chosen by the user.
        """
        return self.chooseFacade.getFacade()
